package com.ruoyi.ldzlsc.service.Impl;


import com.alibaba.nacos.client.naming.utils.CollectionUtils;
import com.ruoyi.common.core.domain.UR;
import com.ruoyi.common.core.exception.ServiceException;
import com.ruoyi.common.core.web.domain.AjaxResult;
import com.ruoyi.common.security.utils.SecurityUtils;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.ldzlsc.domain.ScDispatch;
import com.ruoyi.ldzlsc.domain.ScMaterialIssue;
import com.ruoyi.ldzlsc.domain.ScMaterialIssueDetail;
import com.ruoyi.ldzlsc.domain.ScWorkOrderTask;
import com.ruoyi.ldzlsc.domain.dto.DispatchCreateDto;
import com.ruoyi.ldzlsc.domain.vo.MaterialRequirementVo;
import com.ruoyi.ldzlsc.event.DispatchCreatedEvent;
import com.ruoyi.ldzlsc.mapper.ScDispatchMapper;
import com.ruoyi.ldzlsc.mapper.ScMaterialIssueDetailMapper;
import com.ruoyi.ldzlsc.mapper.ScMaterialIssueMapper;
import com.ruoyi.ldzlsc.mapper.ScMrpDetailMapper;
import com.ruoyi.ldzlsc.mapper.ScWorkOrderTaskMapper;
import com.ruoyi.ldzlsc.service.IScDispatchService;
import com.ruoyi.system.api.BasicService;
import com.ruoyi.system.api.InventoryService;
import org.hibernate.validator.internal.util.stereotypes.Lazy;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 生产派工Service业务层处理
 *
 * @author ldzl
 * @date 2024-07-31
 */
@Service
public class ScDispatchServiceImpl implements IScDispatchService {

    @Autowired
    private ScDispatchMapper scDispatchMapper;

    @Autowired
    private ScWorkOrderTaskMapper scWorkOrderTaskMapper;

    @Autowired
    private ScMaterialIssueMapper scMaterialIssueMapper;

    @Autowired
    private ScMaterialIssueDetailMapper scMaterialIssueDetailMapper;

    @Autowired
    private ScMrpDetailMapper scMrpDetailMapper;

    @Autowired
    private ApplicationEventPublisher applicationEventPublisher;

    @Autowired(required = false)
    private InventoryService inventoryService;
    
    @Autowired
    @Lazy
    private BasicService basicService;
    
    


    /**
     * 创建派工指令
     *
     * @param dto 派工创建数据传输对象
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int createDispatch(DispatchCreateDto dto) {
        // 步骤 1: 数据校验
        ScWorkOrderTask task = validateTaskAndPrerequisites(dto);

        String username = SecurityUtils.getUsername();
        Date nowDate = new Date();

        // 步骤 2: 创建派工指令主记录
        ScDispatch dispatch = createAndSaveDispatch(dto, username, nowDate);

        // 步骤 3: 处理物料和库存
        Long materialIssueId = processMaterialRequests(dto, task, username, nowDate);

        // 步骤 4: 更新工序任务状态
        updateTaskStatusToDispatched(task.getWorkOrderTaskId(), username, nowDate);

        // 步骤 5: 发布派工成功事件
        publishDispatchCreatedEvent(dispatch, materialIssueId, username);

        return 1;
    }

    @Override
    public List<MaterialRequirementVo> getMaterialRequirements(Long workOrderTaskId) {
        return scDispatchMapper.selectMaterialListByTaskId(workOrderTaskId);
    }

    /**
     * 校验工序任务和前置条件
     * @param dto 数据传输对象
     * @return 校验通过的工序任务实体
     */
    private ScWorkOrderTask validateTaskAndPrerequisites(DispatchCreateDto dto) {
        if (inventoryService == null) {
            throw new ServiceException("库存服务不可用，无法进行派工。");
        }

        ScWorkOrderTask task = scWorkOrderTaskMapper.selectScWorkOrderTaskByWorkOrderTaskId(dto.getWorkOrderTaskId());
        if (task == null) {
            throw new RuntimeException("操作失败，工序任务不存在！");
        }
         if (!"0".equals(task.getStatus())) {
            throw new RuntimeException("操作失败，任务状态不是未派工！");
         }
        return task;
    }

    /**
     * 创建并保存派工指令实体
     * @return 持久化的派工指令实体
     */
    private ScDispatch createAndSaveDispatch(DispatchCreateDto dto, String username, Date nowDate) {
        ScDispatch dispatch = new ScDispatch();
        dispatch.setWorkOrderTaskId(dto.getWorkOrderTaskId());
        dispatch.setTeamId(dto.getTeamId());
        dispatch.setLeaderId(dto.getLeaderId());
        if (!CollectionUtils.isEmpty(dto.getMemberIds())) {
            dispatch.setMemberIds(StringUtils.join(dto.getMemberIds(), ","));
        }
        dispatch.setQcDeptId(dto.getQcDeptId());
        dispatch.setQcPersonId(dto.getQcPersonId());
        dispatch.setRemark(dto.getRemark());
        dispatch.setCreateBy(username);
        dispatch.setCreateTime(nowDate);
        scDispatchMapper.insertScDispatch(dispatch);
        return dispatch;
    }

    /**
     * 处理物料申请：创建领料单、锁定库存、保存明细
     * @return 创建的领料单ID
     */
    private Long processMaterialRequests(DispatchCreateDto dto, ScWorkOrderTask task, String username, Date nowDate) {
        ScMaterialIssue materialIssue = new ScMaterialIssue();
        materialIssue.setIssueCode(generateCode(21L));
        materialIssue.setWorkOrderTaskId(dto.getWorkOrderTaskId());
        materialIssue.setWorkOrderId(task.getWorkOrderId());
        materialIssue.setIssueDate(nowDate);
        materialIssue.setStatus("0"); // 0: 待领用
        materialIssue.setCreateBy(username);
        materialIssue.setCreateTime(nowDate);
        scMaterialIssueMapper.insertScMaterialIssue(materialIssue);
        Long materialIssueId = materialIssue.getMaterialIssueId();

        // 如果没有物料申请，直接返回领料单ID
        if (CollectionUtils.isEmpty(dto.getMaterialRequests())) {
            return materialIssueId;
        }

        // 获取物料需求列表以获取 materialCode
        List<MaterialRequirementVo> materialRequirements = scDispatchMapper.selectMaterialListByTaskId(dto.getWorkOrderTaskId());
        Map<Long, String> materialIdToCodeMap = materialRequirements.stream()
                .collect(Collectors.toMap(MaterialRequirementVo::getMaterialId, MaterialRequirementVo::getMaterialCode, (a, b) -> a));

        // 批量锁定库存
        Map<String, BigDecimal> lockRequestMap = new HashMap<>();
        for (DispatchCreateDto.MaterialRequest req : dto.getMaterialRequests()) {
            String materialCode = materialIdToCodeMap.get(req.getMaterialId());
            if (materialCode == null) {
                throw new ServiceException("无法找到物料ID: " + req.getMaterialId() + " 对应的物料编码。");
            }
            lockRequestMap.put(materialCode, req.getQuantity());
        }

        UR lockResult = inventoryService.modifyInventoryBatch(lockRequestMap);
        if (lockResult == null || !lockResult.isFlag()) {
            // 如果锁定失败，立即抛出异常以回滚整个事务
            throw new ServiceException("批量库存锁定失败，请检查库存余量。");
        }

        // 创建领料单明细列表
        List<ScMaterialIssueDetail> details = new ArrayList<>();
        for (DispatchCreateDto.MaterialRequest req : dto.getMaterialRequests()) {
            // 创建领料单明细
            ScMaterialIssueDetail detail = new ScMaterialIssueDetail();
            detail.setMaterialIssueId(materialIssueId);
            detail.setInventoryId(req.getInventoryId());
            detail.setMaterialId(req.getMaterialId());
            detail.setBatchNo(req.getBatchNo());
            detail.setQtyNum(req.getQuantity());
            detail.setCreateBy(username);
            detail.setCreateTime(nowDate);
            details.add(detail);
        }

        // 批量插入明细
        if (!details.isEmpty()) {
            scMaterialIssueDetailMapper.batchInsertScMaterialIssueDetail(details);
        }

        return materialIssueId;
    }

    /**
     * 更新工序任务状态为"已派工"
     */
    private void updateTaskStatusToDispatched(Long workOrderTaskId, String username, Date nowDate) {
        // 1. 首先获取任务详情，查询计划数量
        ScWorkOrderTask task = scWorkOrderTaskMapper.selectScWorkOrderTaskByWorkOrderTaskId(workOrderTaskId);
        if (task == null) {
            throw new ServiceException("工序任务不存在");
        }
        
        // 移除设置初始进度的逻辑，派工时只更新状态，不修改数量
        // 这样可以避免报工时数量限制的问题
        
        // 更新任务状态
        ScWorkOrderTask taskToUpdate = new ScWorkOrderTask();
        taskToUpdate.setWorkOrderTaskId(workOrderTaskId);
        taskToUpdate.setStatus("1"); // 1: 已派工
        // 移除：taskToUpdate.setCompletedQty(initialProgress); // 不再设置初始进度
        taskToUpdate.setUpdateBy(username);
        taskToUpdate.setUpdateTime(nowDate);
        scWorkOrderTaskMapper.updateScWorkOrderTask(taskToUpdate);
        
        System.out.println("派工完成，任务状态已更新为已派工，不设置初始进度");
    }

    /**
     * 发布派工成功事件
     */
    private void publishDispatchCreatedEvent(ScDispatch dispatch, Long materialIssueId, String username) {
        DispatchCreatedEvent event = new DispatchCreatedEvent(
                this,
                dispatch.getDispatchId(),
                dispatch.getWorkOrderTaskId(),
                materialIssueId,
                dispatch.getQcPersonId(),
                username
        );
        applicationEventPublisher.publishEvent(event);
        System.out.println("发布派工成功事件：" + event);
    }

    /**
     * 自动编码
     */
    private String generateCode(Long type) {
        // 调用远程服务（消费模式，确保每次调用都会递增流水号）
        AjaxResult codeResult = basicService.automaticallyNumbersConsume(type);

        // 确保远程调用返回了结果对象
        if (codeResult == null) {
            throw new ServiceException(String.format("远程获取编码调用失败，返回结果为空，类型: %d", type));
        }

        // 根据该接口的特殊设计，编码直接存放在msg字段
        Object msg = codeResult.get(AjaxResult.MSG_TAG);
        Object code = codeResult.get(AjaxResult.CODE_TAG);
        System.out.println("编码是：" + code);
        // 确保编码内容不为空
        if (msg == null || msg.toString().isEmpty()) {
            throw new ServiceException(String.format("远程获取编码成功，但返回的编码为空，类型: %d, 返回信息: %s", type, msg));
        }

        // 从AjaxResult中正确取出msg部分作为编码
        return msg.toString();
    }
} 